package com.android.baseconfig.base;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Environment;

import com.android.baseconfig.common.utils.FileUtil;
import com.android.baseconfig.common.utils.Logc;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序
 */
public final class CrashHandler implements UncaughtExceptionHandler {
    /**
     * <p>
     * (域的意义、目的、功能)
     * </p>
     */
    private static final int CRASH_FILE_MAX_SIZE = 10;

    public static final String TAG = CrashHandler.class.getName();

    /**
     * 系统默认的UncaughtException处理类
     */
    private UncaughtExceptionHandler mDefaultHandler;

    /**
     * CrashHandler实例
     */

    private static CrashHandler crashInstance = new CrashHandler();

    /**
     * 程序的Context对象
     */
    private Context mContext;

    /**
     * 用来存储设备信息和异常信息
     */
    private Map<String, String> infos = new HashMap<String, String>();

    /**
     * 保证只有一个CrashHandler实例
     */
    private CrashHandler() {
    }

    /**
     * 获取CrashHandler实例 ,单例模式
     */
    public static CrashHandler getInstance() {
        return crashInstance;
    }

    /**
     * 初始化
     *
     * @param context
     */
    public void init(Context context) {
        mContext = context;
        // 获取系统默认的UncaughtException处理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        // 设置该CrashHandler为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
     * 当UncaughtException发生时会转入该函数来处理
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if (!handleException(ex) && mDefaultHandler != null) {
            // 如果用户没有处理则让系统默认的异常处理器来处理
            mDefaultHandler.uncaughtException(thread, ex);
            Logc.d(TAG, "THE END");
        } else {
            try {
                TimeUnit.MILLISECONDS.sleep(1000);
            } catch (InterruptedException e) {
                Logc.e(TAG, "error : ", e);
            }

            BaseConfigApplication.getInstance().exit();
            // 退出程序
            android.os.Process.killProcess(android.os.Process.myPid());
            // mDefaultHandler.uncaughtException(thread, ex);
        }

    }

    /**
     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
     *
     * @param ex
     * @return true:如果处理了该异常信息;否则返回false.
     */
    private boolean handleException(Throwable ex) {
        if (ex == null) {
            return false;
        }
        // 使用Toast来显示异常信息
        // ThreadUtils.execute(new Runnable() {
        //
        //     @Override
        //     public void run() {
        //         Looper.prepare();
        //         ToastUtils.getInstance().showToast(BaseConfigApplication.getInstance(), R.string.systemerror, Toast.LENGTH_LONG);
        //         Looper.loop();
        //     }
        // });

        // try {
        //     Thread.sleep(1000L);
        // } catch (InterruptedException e) {
        //     e.printStackTrace();
        // }

        Logc.e(TAG, ex.toString(), ex);
        // 收集设备参数信息
        collectDeviceInfo(mContext);
        // 保存日志文件
        saveCrashInfo2File(ex);

        // 删除多余的文件
        deleteOldLogFile();

        BaseConfigApplication.getInstance().exit();
        // 检测异常后直接退出应用程序
        android.os.Process.killProcess(android.os.Process.myPid());

        return true;
    }

    /**
     * 删除多余的crash文件
     *
     */
    private void deleteOldLogFile() {
        Logc.d(TAG, "start delete file");

        try {
            String folderPath = getLogFolderPath();
            File parentFile = new File(folderPath);
            if (!parentFile.exists()) {
                Logc.d(TAG, "crash floder is no exist");
                return;
            }

            File[] fileList = parentFile.listFiles();

            int fileSize = fileList.length;

            if (fileSize <= CRASH_FILE_MAX_SIZE) {
                Logc.d(TAG, "log file size is less then 10, cuttren size = " + fileSize);
                return;
            }

            Map<Long, Object> filePathMap = new HashMap<Long, Object>();
            long[] fileTimeArr = new long[fileSize];

            for (int i = 0; i < fileSize; i++) {
                File file = fileList[i];
                filePathMap.put(file.lastModified(), file.getPath());
                fileTimeArr[i] = file.lastModified();
            }

            Arrays.sort(fileTimeArr);

            int needDeleteSize = fileSize - CRASH_FILE_MAX_SIZE;
            Logc.d(TAG, "delete crash file size = " + needDeleteSize);

            for (int i = 0; i < needDeleteSize; i++) {
                String path = filePathMap.get(fileTimeArr[i]).toString();
                File file = new File(path);
                if (!file.exists()) {
                    continue;
                }
                if (!file.delete()) {
                    Logc.d(TAG, "file delete fail, path =" + path);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 收集设备参数信息
     *
     * @param ctx
     */
    public void collectDeviceInfo(Context ctx) {
        try {
            PackageManager pm = ctx.getPackageManager();
            PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
            if (pi != null) {
                String versionName = pi.versionName + "";
                String versionCode = pi.versionCode + "";
                infos.put("versionName", versionName);
                infos.put("versionCode", versionCode);
            }
        } catch (NameNotFoundException e) {
            Logc.e(TAG, "an error occured when collect package info", e);
        }
    }

    /**
     * 保存错误信息到文件中
     *
     * @param ex
     * @return 返回文件名称, 便于将文件传送到服务器
     */
    private String saveCrashInfo2File(Throwable ex) {

        StringBuffer sb = new StringBuffer();
        for (Map.Entry<String, String> entry : infos.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            sb.append(key + "=" + value + "\r\n");
        }

        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);

        ex.printStackTrace(printWriter);

        Throwable cause = ex.getCause();

        while (cause != null) {
            cause.printStackTrace(printWriter);
            cause = cause.getCause();
        }

        printWriter.close();
        String result = writer.toString();
        sb.append(result);

        FileOutputStream fos = null;

        try {
            long timestamp = System.currentTimeMillis();
            // 用于格式化日期,作为日志文件名的一部分
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
            String time = formatter.format(new Date());

            // crash日志文件名
            String fileName = "crash-" + time + "-" + timestamp + ".log";

            String path = getLogFolderPath();

            File dir = new File(path);
            // Logc.d(TAG, "path=" + path + " is or not a directory = " +
            // dir.isDirectory());
            if (!dir.exists()) {
                boolean mkd = dir.mkdirs();
                if (!mkd) {
                    Logc.d(TAG, "Create directory failed!!!!!!!!");
                }
            }
            File file = new File(path + fileName);
            if (!file.exists()) {
                file.createNewFile();
            }
            // FileOutputStream fos = new FileOutputStream(path + fileName);
            fos = new FileOutputStream(path + fileName);
            fos.write(sb.toString().getBytes("UTF-8"));
            fos.flush();
            fos.close();
            return fileName;
        } catch (FileNotFoundException e) {
            Logc.e(TAG, "not found the file.", e);
        } catch (IOException e) {
            Logc.e(TAG, "an error occured while writing file...", e);
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    Logc.e(TAG, "", e);
                }
            }

        }

        return null;
    }

    /**
     * 获取日志存储的文件夹的路径
     *
     * @return
     */
    private String getLogFolderPath() {
        // 保存日志文件的相对目录
        String savePath = "Download/苏泊尔" + File.separator + "crash" + File.separator;
        // 把内部存储作为默认根目录
        String defaultRootPath = mContext.getFilesDir().getAbsolutePath();
        // 如果有外部存储就用外部存储
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            defaultRootPath = FileUtil.getExternalStoragePath();
        }

        // 先用相对路径作为日志保存目录
        String path = savePath;
        // 然后再获得一个绝对目录替换为保存目录
        if (defaultRootPath != null) {
            if (defaultRootPath.endsWith(File.separator)) {
                path = defaultRootPath + savePath;
            } else {
                path = defaultRootPath + File.separator + savePath;
            }
        }
        return path;
    }
}
